今天來稍微講解javascript的物件參考使用上的一些心得,主要是針對Gray Code在進行修改時的過程紀錄,希望可以對大家有一些幫助~
用Javascript征服演算法 (2-Gray Code-Javascript<番外篇>)
此篇的產生,主要是因為在“用Javascript征服演算法 (2-Gray Code-Javascript實作)“小賴大大有回應我可以改進實作code,而後我確實也將程式碼做了修正,在這過程中有一些心得想與大家分享,也是希望能夠記錄下來:)
在“用Javascript征服演算法 (2-Gray Code-Javascript實作)“中,主要是針對產生bit數變化時的function的部分,能夠有更佳有效率的方式,以下是當初的function寫法
Gray.prototype.next = function() {
//奇數列i就表示為最後一位元(length - 1)
//偶數列就是最右往左邊數為1得數字的左邊那個(所以減1位數)
//-1則是在Gray code最後一組只有最左邊的數字為1其餘為0的狀況下,lastIndexOf(1)才會為0
var i = (this.isOdd ? this.code.length : this.code.lastIndexOf(1)) - 1;
console.log(this.code.lastIndexOf(1));
return new Gray(i === -1 ? [] :
this.code.slice(0, i)
.concat([1 - this.code[i]])
.concat(this.code.slice(
i + 1, this.code.length)),
!this.isOdd);
};
而小賴是回應我,能夠將return中的新array產生方是稍作修改,就可以少做兩次concat的動作,而小賴大大提供的修改方式如下(順便特別圈出要修改的地方)
修改前
this.code.slice(0, i)
.concat([1 - this.code[i]])
.concat(this.code.slice(i + 1, this.code.length))
修改後
var newcode = this.code.slice(0);
newcode[i] = 1-newcode[i];
但我這個人就是皮在癢,很想精簡程式 (雖然也被糾正發現其實沒差XD),就改成了以下程式碼
this.code = this.code.slice(0);
this.code[i] = 1 - this.code[i];
為什麼我想用this.code? 而不想去產生一個var newcode? 而且直接使用this.code會造成什麼問題?
因為之前本身在寫其他語言時,就很習慣類似i = i+1這種寫法,就想避免使用新變數
但其實,在這邊
this.code = this.code.slice(0);
var newcode = this.code.slice(0);
this.code跟直接宣告newcode的都是對原本this.code做所謂的 "深層複製"
何謂深層複製? 何謂淺層?
在這邊深層複製其實指的就是,產生並使用一塊新的記憶體並複製this.code在heap中參考所指向的那塊記憶體 (也就是真正的資料所在處)
而Slice這個函式就是提供對array作出深層複製(如下圖),所以其實都是做同樣的事情,只是多宣告一個var newcode但其實並沒有比較節省記憶體或多大差別QQ
此外使用this.code還會造成初始化的this.code陣列[0,0,0,0]被修改的問題
從上圖可以看見有一個紅色框框,那個其實是一開始this.code的參考記憶體位置,但因為我們做了this.code = this.code.slice(0),所以this.code指到另外一個記憶體位置了,而接下來我們又馬上將[0,0,0,0] 修改成 [0,0,0,1] 因此原本的[0,0,0,0]就徹底被蓋過去了orz
接下來,來講解淺層複製吧~
何謂淺層複製?
淺層層複製是僅複製了參考,也就是只將物件本身的參考位置複製到新的變數,也就是他們都指向同一塊記憶體,如下圖
如果我們當初沒有做slice這動作,而是做
var newcode = this.code;
//or省略this.code = this.cdoe.slice(0);
就會完美呈現上圖的囧境,因為沒有產生新的記憶體去複製參考,因此接下來怎麼修改bit(0->1 or 1->0)的過程都會被重複修改到上一次修改的值,因此最後alert出來的結果都會是最後一個結果[1,0,0,0] orz
以上就是整體修改得debug過程,使用javascript時要非常注意物件的行別,因為他會做自動轉換,如果沒有小心注意物件所傳遞的是參考or值,就很可能會發生上述悲劇QQ
希望本篇心得文,有幫助到各位對於記憶體的小小觀念,在此下台一鞠躬~
可以進一步思考一個問題,如果陣列裡頭存的是物件呢? 那用slice會怎麼樣呢? 要真的做到連裡頭存的object都複製的深深層複製,要怎麼做呢?